<?php

namespace App\Models;

use App\Models\Concerns\DeSensitive;
use App\Models\Coupon\CouponCard;
use App\Models\OpenWechat\WechatUser;
use App\Observers\Contract\ObserverCleanable;
use App\Observers\OSSCleanObserver;
use App\Services\Utils\OSSable;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Http\UploadedFile;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject as AuthenticatableUserContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

/**
 * Class User - 用户
 *
 * @property string $password
 * @property string $avatar
 *
 * @property-read WechatUser $wechatUser
 * @property-read Collection|UserDevice[] $userDevices
 * @property-read Collection|UserDevicePreset[] $userDevicePresets
 *
 * @package App\Models
 */
class User extends Authenticatable implements
    CanResetPasswordContract,
    AuthenticatableUserContract,
    ObserverCleanable
{
    use Notifiable,
        DeSensitive,
        OSSable;

    protected $table = 'users';

    const GUARD_NAME = 'app_user';

    protected static $unguarded = true;

    protected $oss_columns = [ 'avatar' ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    protected $casts = [
        'sex'       => 'int',
        'area_code' => 'string',
        'nick_name' => 'string',
        'phone'     => 'string',
    ];

    protected $avatar_key_prefix = 'user/avatar/';

    public static function findByAreaCodePhone(string $area_code, string $phone): ?self
    {
        return (new self)->where(compact('area_code', 'phone'))->first();
    }

    public static function findByEmail(string $email): ?self
    {
        return (new self)->where(compact('email'))->first();
    }

    /**
     * @return mixed
     */
    public function getJWTIdentifier(): int
    {
        return $this->getKey(); // Eloquent model method
    }

    /**
     * @return array
     */
    public function getJWTCustomClaims(): array
    {
        return [];
    }

    /**
     * @param $value
     * @return mixed
     */
    public function setPasswordAttribute(?string $value): void
    {
        if ($value) {
            $this->attributes['password'] = bcrypt($value);
        }
    }

    /**
     * @used-by $wechatUser
     * @return hasOne
     */
    public function wechatUser(): hasOne
    {
        return $this->hasOne(WechatUser::class, 'user_id', 'id');
    }

    /**
     * @used-by $userDevices
     * @return HasMany
     */
    public function userDevices(): HasMany
    {
        return $this->hasMany(UserDevice::class, 'user_id', 'id');
    }

    /**
     * @used-by $userDevicePresets
     * @return HasMany
     */
    public function userDevicePresets(): HasMany
    {
        return $this->hasMany(UserDevicePreset::class, 'user_id', 'id');
    }

    /**
     * @user-by $userSkinRecords
     * @return HasMany
     */
    public function userSkinRecords()
    {
        return $this->hasMany(SkinRecords::class, 'user_id', 'id');
    }

    /**
     * @param string|UploadedFile $avatar
     *
     * @return void
     * @throws \Exception
     */
    public function setAvatarAttribute($avatar): void
    {
        if (is_file($avatar)) {
            $new_avatar = $this->uploadPostFile($avatar, $this->avatar_key_prefix);
            $this->attributes['avatar'] = $new_avatar;
        } else if (is_string($avatar) && strlen($avatar)) {
            $this->attributes['avatar'] = $avatar;
        }
    }

    /**
     * @return string|null
     * @throws \Exception
     */
    public function getAvatarAttribute(): ?string
    {
        return $this->attributeToStaticUrl('avatar');
    }

    /**
     * 注册删除 bosKey 事件绑定
     */
    protected static function boot()
    {
        parent::boot();

        /**
         * 监听bos清理事件
         */
        self::observe(OSSCleanObserver::class);
    }

    /**
     * @return array
     */
    public function keyCleanColumns(): array
    {
        return $this->oss_columns;
    }

    public function couponCards(): HasMany
    {
        return $this->hasMany(CouponCard::class, 'user_id', 'id');
    }

    public function userScores(): HasMany
    {
        return $this->hasMany(UserScore::class, 'user_id', 'id');
    }

    public function userActions(): HasMany
    {
        return $this->hasMany(UserAction::class, 'user_id', 'id');
    }

    public function wishLists(): HasMany
    {
        return $this->hasMany(WishList::class, 'user_id', 'id');
    }
}
